 /*
 * Copyright (c) 2011 Mellanox Technologies Ltd.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. Neither the name of the Mellanox Technologies Ltd nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 *
 */

#include "os_abstract.h"

#include <string.h>
#include <stdio.h>


void os_printf_backtrace(void)
{
#ifdef WIN32
     unsigned int   i;
     void         * stack[ 100 ];
     unsigned short frames;
     SYMBOL_INFO  * symbol;
     HANDLE         process;

     process = GetCurrentProcess();

     SymInitialize( process, NULL, TRUE );

     frames               = CaptureStackBackTrace( 0, 100, stack, NULL );
     symbol               = ( SYMBOL_INFO * )calloc( sizeof( SYMBOL_INFO ) + 256 * sizeof( char ), 1 );
     symbol->MaxNameLen   = 255;
     symbol->SizeOfStruct = sizeof( SYMBOL_INFO );

     for( i = 0; i < frames; i++ )
     {
         SymFromAddr( process, ( DWORD64 )( stack[ i ] ), 0, symbol );

         printf( "%i: %s - 0x%0X\n", frames - i - 1, symbol->Name, symbol->Address );
     }

     free( symbol );
#else
	char **strings;
	void* m_backtrace[25];
	int m_backtrace_size = backtrace(m_backtrace, 25);
	printf("sockperf: [tid: %lu] ------\n", (unsigned long)os_getthread().tid);
	strings = backtrace_symbols(m_backtrace, m_backtrace_size);
	for (int i = 0; i < m_backtrace_size; i++)
		printf("sockperf: [%i] %p: %s\n", i, m_backtrace[i], strings[i]);
	free(strings);
#endif
}


//Thread functions

void os_thread_init (os_thread_t * thr)
{
#ifdef WIN32
	thr->hThread = NULL;
	thr->tid = 0;
#else
	thr->tid = 0;
#endif
}

void os_thread_close (os_thread_t * thr)
{
#ifdef WIN32
	if (thr->hThread)
		CloseHandle (thr->hThread);
#endif
}


void os_thread_detach (os_thread_t * thr)
{
#ifndef WIN32
	pthread_detach (thr->tid);
#endif
}

int os_thread_exec (os_thread_t * thr, void *(*start) (void *), void *arg)
{
#ifdef WIN32
	if (thr->hThread)
		CloseHandle (thr->hThread);
	thr->hThread = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE) start,
								 arg, 0, &thr->tid);
	if (thr->hThread == INVALID_HANDLE_VALUE) {
		return -1;
	}
	return 0;
#else
	pthread_attr_t atts;
	pthread_attr_init (&atts);
#ifdef HAVE_PTHREAD_ATTR_SETSCOPE
	pthread_attr_setscope (&atts, PTHREAD_SCOPE_SYSTEM);
#endif
#ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE
	pthread_attr_setstacksize (&atts, 256 * 1024);
#endif
	return pthread_create (&thr->tid, &atts, start, arg);
#endif

}

void os_thread_kill (os_thread_t * thr)
{
#ifdef WIN32
	DWORD exit_code;
	if (GetExitCodeThread (thr, &exit_code))
			TerminateThread(thr, exit_code); // kill thread
#else
	pthread_kill(thr->tid, SIGINT);
#endif
}

void os_thread_join (os_thread_t *thr)
{
#ifdef WIN32
				WaitForSingleObject(thr ,INFINITE); 
#else
				pthread_join(thr->tid, 0);
#endif
}

os_thread_t os_getthread(void)
{
	os_thread_t mythread;
#ifdef WIN32
	mythread.tid = GetCurrentThreadId();
	mythread.hThread = GetCurrentThread();
#elif __FreeBSD__
	mythread.tid = pthread_self();
#else
	mythread.tid = syscall(__NR_gettid);
#endif
	return mythread;
}

// Mutex functions

void os_mutex_init (os_mutex_t * lock)
{
#ifdef WIN32
	lock->mutex = CreateMutex (NULL, FALSE, NULL);
#else
	pthread_mutex_init (&lock->mutex, NULL);
#endif
}

void os_mutex_close (os_mutex_t * lock)
{
#ifdef WIN32
	CloseHandle (lock->mutex);
#else
	pthread_mutex_destroy (&lock->mutex);
#endif
}

void os_mutex_lock (os_mutex_t * lock)
{
#ifdef WIN32
	WaitForSingleObject (lock->mutex, INFINITE);
#else
	pthread_mutex_lock (&lock->mutex);
#endif
}

void os_mutex_unlock (os_mutex_t * lock)
{
#ifdef WIN32
	ReleaseMutex (lock->mutex);
#else
	pthread_mutex_unlock (&lock->mutex);
#endif
}


int os_set_nonblocking_socket(int fd)
{
#ifdef WIN32
	int iResult;
	u_long iMode = 1; // = Non-blocking
	iResult = ioctlsocket(fd, FIONBIO, &iMode);
	if (iResult != NO_ERROR) {
		printf("ERROR: ioctlsocket failed with error: %ld\n", iResult);
		return -1;
	}
	return 0;
#else
	int flags, ret;
	flags = fcntl(fd, F_GETFL);
	if (flags < 0) {
		printf("ERROR: fcntl(F_GETFL)\n");
		return -1;
	}
	flags |=  O_NONBLOCK;
	ret = fcntl(fd, F_SETFL, flags);
	if (ret < 0) {
		printf("ERROR: fcntl(F_SETFL)\n");
		return -1;
	}
	return 0;
#endif
}

void os_set_signal_action(int signum, sig_handler handler)
{
#ifdef WIN32
	// Meny: No SIGALRM on Windows, A different thread handles test duration timer.
	signal(signum, handler);
#else
	static struct sigaction sigact;//Avner: is 'static' needed?

	sigact.sa_handler = handler;
	sigemptyset(&sigact.sa_mask);
	sigact.sa_flags = 0;

	sigaction(signum, &sigact, NULL);
#endif

}

int os_daemonize()
{
#ifdef WIN32
	printf("ERROR: daemonize is not supported!\n");
	return -1;
#else
	if (daemon(1, 1)) {
		return -1;
	} else {
		return 0;
	}
#endif
}

#ifdef WIN32
	struct TimeHandler {
		itimerval myTimer;
		sig_handler *handler;

		TimeHandler(itimerval _Timer, sig_handler _handler){
			this->myTimer = _Timer;
			this->handler = _handler;
		}
	} ;
#endif

int os_set_duration_timer(const itimerval &timer, sig_handler handler)
{
	int ret;
#ifdef WIN32
	// Creating a new thread to handle timer logic
	os_thread_t timer_thread;
	os_thread_init (&timer_thread);

	TimeHandler *pTimeHandler = new TimeHandler(timer, handler);

	ret =  os_thread_exec (&timer_thread, win_set_timer, (void *)pTimeHandler);
	if (ret) {
		printf("ERROR: win_set_timer() failed\n");
		return -1;
	}
#else
	os_set_signal_action(SIGALRM, handler);
	ret = setitimer(ITIMER_REAL, &timer, NULL);
	if (ret) {
		printf("ERROR: setitimer() failed\n");
		return -1;
	}
#endif
		return 0;
}

#ifdef WIN32
// Meny: This function contains test duration timer logic
void* win_set_timer(void *_pTimeHandler)
{
	TimeHandler *pTimeHandler = (TimeHandler*)_pTimeHandler;
	itimerval timer = pTimeHandler->myTimer;

	HANDLE hTimer = CreateWaitableTimer (NULL,FALSE, NULL);
	if (!hTimer)
		printf("ERROR: Create timer failed\n");

	LARGE_INTEGER liDueTime;

	// Create an integer that will be used to signal the timer 
	__int64 qwDueTime = -(((timer.it_value.tv_sec) * _SECOND) + (timer.it_value.tv_usec) * 10);

	// Copy the relative time into a LARGE_INTEGER.
	liDueTime.LowPart  = (DWORD) ( qwDueTime & 0xFFFFFFFF );
	liDueTime.HighPart = (LONG)  ( qwDueTime >> 32 );

	int bSuccess = SetWaitableTimer(
			hTimer,           // Handle to the timer object
			&liDueTime,       // When timer will become signaled
			0,				  // Periodic timer 
			0,				  // Completion routine
			NULL,	          // Argument to the completion routine
			FALSE );          // Do not restore a suspended system

	if (WaitForSingleObject(hTimer, INFINITE) != WAIT_OBJECT_0)
        printf("WaitForSingleObject failed (%d)\n", GetLastError());

	pTimeHandler->handler(SIGALRM);

	CloseHandle(hTimer);

	return 0;
}
#endif

bool os_sock_startup()
{
#ifdef WIN32
	WSADATA wsaData;
	if (WSAStartup(0x202, &wsaData) != 0){
		return false;
	}
	return true;
#else
	return true;
#endif
}

bool os_sock_cleanup()
{
#ifdef WIN32
	if (WSACleanup() != 0){
		return false;
	}
	return true;
#else
	return true;
#endif
}

void os_init_cpuset(os_cpuset_t *_mycpuset)
{
#ifdef WIN32
	_mycpuset->cpuset = 0;
#else
	CPU_ZERO(&_mycpuset->cpuset);
#endif
}

void os_cpu_set(os_cpuset_t *_mycpuset, long _cpu_from, long _cpu_cur)
{

	while ((_cpu_from <= _cpu_cur)) {
#ifdef WIN32
		_mycpuset->cpuset = (int)( 1 <<  _cpu_from);
#else
		CPU_SET(_cpu_from, &(_mycpuset->cpuset));
#endif
		_cpu_from++;
	}
}

int os_set_affinity(const os_thread_t & thread, const os_cpuset_t &_mycpuset)
{
#ifdef WIN32
		if (0 == SetThreadAffinityMask(thread.hThread, _mycpuset.cpuset))
			return -1;
#else
		// Can't use thread.tid since it's syscall and not pthread_t
		if (0 != pthread_setaffinity_np(pthread_self(), sizeof(os_cpuset_t), &(_mycpuset.cpuset)))
			return -1;
#endif
		return 0;
}

int os_get_max_active_fds_num() {
#ifdef WIN32
	return MAX_OPEN_FILES;
#else
	static int max_active_fd_num = 0;
	if (!max_active_fd_num) {
		struct rlimit curr_limits;
		if (getrlimit (RLIMIT_NOFILE, &curr_limits) == -1) {
			perror ("getrlimit");
			return 1024; // try the common default
		}
		max_active_fd_num = (int) curr_limits.rlim_max;
	}
	return max_active_fd_num;
#endif
}
